package cloud.pangui.core.db;

import cloud.pangui.core.spring.SpringUtils;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 单数据源或多数据源公共配置
 * spring.datasource.
 * username
 * password
 * ...
 * 多数据源配置
 * spring.datasource.
 * usercenter.
 * username
 * password
 * teachercenter.
 * username
 * password
 *
 * @Auther hepangui
 * @Date 2020/10/06 0001
*/
@Configuration
public class MybatisPlusRegistryPostProcressor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {

	private static final String DATASOURCE_PREFIX = "spring.datasource.";
	private static final String DATASOURCE_URL = "url";

	private static final Pattern urlPattern = Pattern.compile("spring\\.datasource\\.([^.]+)(\\.url|jdbcUrl)");
	public static final String DEFAULT_DATASOURCE_NAME = "default";

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		Map<String, Properties> dbConfigs = buildDbConfig();

		for (Map.Entry<String, Properties> configEntry : dbConfigs.entrySet()) {
			String name = configEntry.getKey();
			Properties properties = configEntry.getValue();

			registry.registerBeanDefinition("datasource_" + name,
					BeanDefinitionBuilder.genericBeanDefinition(DataSource.class,
							() -> new HikariDataSource(new HikariConfig(properties))
					).getRawBeanDefinition());

			registry.registerBeanDefinition("paginationInnerInterceptor_" + name,
					BeanDefinitionBuilder.genericBeanDefinition(PaginationInnerInterceptor.class).getRawBeanDefinition()
			);

			registry.registerBeanDefinition("sqlSessionFactory_" + name,
					BeanDefinitionBuilder.genericBeanDefinition(MybatisPlusInterceptor.class)
							.addPropertyReference("dataSource", "dataSource_" + name)
							.addPropertyReference("innerInterceptors", "paginationInnerInterceptor_" + name)
							.addPropertyReference("innerInterceptors", "paginationInnerInterceptor_" + name)
							.addPropertyValue("mapperLocations", "paginationInnerInterceptor_" + name)
							.getRawBeanDefinition()
			);

			registry.registerBeanDefinition("countSqlParser_" + name,
					BeanDefinitionBuilder.genericBeanDefinition(JsqlParserCountOptimize.class)
							.addPropertyValue("optimizeJoin", true).getRawBeanDefinition()
			);

			String basePackage = DEFAULT_DATASOURCE_NAME.equals(name) ? properties.getProperty("mybatis.basePackage") : properties.getProperty("mybatis." + name + ".basePackage");
			if (!StringUtils.isEmpty(basePackage)) {
				registry.registerBeanDefinition("mapperScannerConfigurer_" + name,
						BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class)
								.addPropertyValue("basePackage", basePackage).getRawBeanDefinition()
				);
			}
		}
	}


	private Map<String, Properties> buildDbConfig() {
		Properties properties = SpringUtils.getProperties();
		Enumeration<String> enumeration = (Enumeration<String>) properties.propertyNames();
		Map<String, Properties> dbConfigs = new HashMap<>();
		// 获取所有的数据源
		while (enumeration.hasMoreElements()) {
			String key = enumeration.nextElement();
			Matcher matcher = urlPattern.matcher(key);
			if (matcher.matches()) {
				String name = matcher.group(1);
				dbConfigs.put(name, new Properties());
				// HikariConfig中使用jdbcUrl
				properties.setProperty(key.replace(".url", ".jdbcUrl"), properties.getProperty(key));
			}

		}
		if (dbConfigs.size() == 0) {
			dbConfigs.put(DEFAULT_DATASOURCE_NAME, new Properties());
		}
		enumeration = (Enumeration<String>) properties.propertyNames();
		Set<String> datasourceNames = dbConfigs.keySet();
		while (enumeration.hasMoreElements()) {
			String key = enumeration.nextElement();
			if (key.startsWith(DATASOURCE_PREFIX)) {
				boolean isDefault = true;
				for (String datasourceName : datasourceNames) {
					if (key.startsWith(DATASOURCE_PREFIX + datasourceName + ".")) {
						isDefault = false;
						dbConfigs.get(datasourceName).setProperty(key.replace(DATASOURCE_PREFIX + datasourceName + ".", ""), properties.getProperty(key));
					}
				}
				// 如果是默认配置，要给所有dbConfig加上
				if (isDefault) {
					for (String datasourceName : datasourceNames) {
						Properties dbP = dbConfigs.get(datasourceName);
						if (StringUtils.isEmpty(dbP.getProperty(key))) {
							dbP.setProperty(key, properties.getProperty(key));
						}
					}
				}
			}
		}
		return dbConfigs;
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

	}

	@Override
	public int getOrder() {
		return -1;
	}
}
